package utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.biojava.bio.structure.*;
import rotlib.RotamerLibrary;
import rotlib.RotamerLibraryCollection;
import rotlib.io.RotamerLibraryReader;

/**
 *  Class for testing the algorithms to calculate collisions
 *  @author Brizuela C.A., Beltran A., Zatarain H.
 *  @version 1.1.0
 */
public class AminoacidsGrid {
    public HashMap<String, CellGeneric> Grid;
    public RotamerLibraryCollection rlc;
    public HashMap<Integer,AminoacidExtended> aminoacidMap;
    
    public AminoacidsGrid(){
        Grid = new HashMap<>();
        rlc = RotamerLibraryReader.readRotamerLibrary(RotamerLibraryCollection.DUN_IND_ROT_LIB_TYPE);
    }
    
    /**
    * Obtain all the aminoacids from a structure and generates a hashMap with them
    * @param s the Structure object that is readed from a pdb file
    * @return a container of the aminoacids colliding with an atom <hashcode of the atom, Array of atoms that collide>.
    */
    public HashMap<Integer,AminoacidExtended> obtainAminoacids(Structure structure, Solution sol){
        //A container that is going to store all the aminoacids in the structure
        aminoacidMap = new HashMap<>();
        ArrayList<Atom> atoms = new ArrayList<>();
        AminoacidExtended aminoExt;
        AminoAcid aminoacid;
        Group atomGroups;
        Chain chain;
        int iHashCode = 0, key = 0;
        double ratio = 0.0;
        
        for (int chainIndex = 0; structure != null && structure.getChains() != null && chainIndex < structure.getChains().size(); chainIndex++) {
            chain = structure.getChain(chainIndex);
            for (int resIndex = 0; chain != null && chain.getAtomGroups() != null && resIndex < chain.getAtomLength(); resIndex++)  {
                atomGroups = chain.getAtomGroup(resIndex);
                if (atomGroups instanceof AminoAcid) {
                    try {
                        aminoacid = (AminoAcid) structure.getChain(chainIndex).getAtomGroup(resIndex);
                        iHashCode = aminoacid.hashCode();
                        key = Integer.parseInt(aminoacid.getPDBCode());
                        if(aminoacidMap.containsKey(key))//if(aminoacidMap.containsKey(iHashCode))
                            System.out.println("Repeteaded_AminoacidMap!!"+aminoacid.getPDBName() + "\t" + aminoacid.getPDBCode() + "\tHashCode:\t"+iHashCode);
                        //System.out.println(aminoacid.getPDBName() + "\t" + aminoacid.getPDBCode() + "\tHashCode:\t"+iHashCode);
                        
                        //obtiene el carbono beta y del atomo mas distante, asi como la distancia entre ellos para darselo como entrada al objeto AminoacidExtended
                        atoms = getCentralAtomAndMoreDistantAtom(aminoacid);
                        
                        //obtaining the actual ratio
                        //ratio = Calc.getDistance(atoms.get(0), atoms.get(1));
                        //obtaining the ratio predefined by the maxRotamer
                        ratio = ListUtil.aminoRadius(aminoacid.getPDBName());
                        //System.out.println(aminoacid.getPDBName()+"\t"+ratio);
                        
                        //Creates the extended aminoacid and add to the aminoacid Map
                        aminoExt = new AminoacidExtended(aminoacid, atoms.get(0), atoms.get(1), ratio, "", 0.0 /*getRotamerFrequencyRASP(aminoacid.getPDBName(),Integer.parseInt( sol.getRomaterVector().get(0).get(key-1) ))*/);
                        aminoacidMap.put(key, aminoExt);
                    } catch (Exception ex) {
                        //Marca excepcion cuando no existe el carbono beta
                        Logger.getLogger(AminoacidsGrid.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
       
        return aminoacidMap;
    } 
   
    
    /**add by armando **/
    public double getRadiusAmino(Atom centralAtom, Atom moreDistantAtom) throws StructureException {
        double radius;
        radius = Calc.getDistance(centralAtom, moreDistantAtom);
        
        return radius; 
    }
    
    /**
    * Obtain the central atom of the aminoacid (beta carbon) and the more distant atom from the central atom
    * @param aminoacid the aminoacid that is going to be evaluated 
    * @return an array with two the atoms, the central atom of the aminoacid (generally beta carbon) and the more distant atom from the side chain
    */    
    public static ArrayList<Atom> getCentralAtomAndMoreDistantAtom(AminoAcid aminoacid){
        int aminoacidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aminoacid.getPDBName());
        double maxDistance =  Double.NEGATIVE_INFINITY, distance = 0;      
        String [] atomList = ListUtil.ATOM_LIST[aminoacidIndex];
        Atom bcAtom = null, mdAtom = null, tempAtom = null;
        ArrayList<Atom> atoms = new ArrayList<>();
                
        try{
            if(aminoacidIndex==7){//7 if the aminoacid is Glicine (GLY)
                mdAtom = aminoacid.getAtom(atomList[0]);        //.getAtom("N");
                bcAtom = aminoacid.getAtom(atomList[1]);        //.getAtom("CA");
            } else {
                bcAtom = aminoacid.getAtom(atomList[4]);        //.getAtom(CB");
                if(aminoacidIndex==0){//0 if the aminoacid is Alanine (ALA)
                    mdAtom = aminoacid.getAtom(atomList[0]);    //.getAtom("N");
                } else {
                    for(int i=0; i<atomList.length; i++){
                        if(i==4)    //Doesnt compare against CB, because distance is 0
                            continue;
                        tempAtom = aminoacid.getAtom(atomList[i]);
                        distance = Calc.getDistance(bcAtom, tempAtom);
                        if(maxDistance < distance){
                            maxDistance = distance;
                            mdAtom = tempAtom;
                        }
                    }
                }
            }
        }catch(Exception ex){
            System.err.print(ex);
        }
        atoms.add(bcAtom);
        atoms.add(mdAtom);
        
        return atoms;
    }
    
   /**
    * Create a hashMap that represent the Grid (Checkerboard, Sieve) where a cell only exists if have at least one atom
    * @param Aminoacids the collection of aminoacids to insert in the Grid
    * @param intervals an array with the size of each side of the cell
    * @param originPoints the points x,y,z that are the minimum in each dimension and that serve as origin for the Grid
    * @param intervalMethod the method that defines how to create the Grid with the interval [false: floor(x-x_0)/delta | true: floor(x/delta)]
    * @return a container of the cells in the grid, where each cell contains all atoms in the interval
    */
    public HashMap<String, CellGeneric> createGrid(Collection<AminoacidExtended> Aminoacids, double interval){
        /*HashMap<String, CellGeneric>*/ Grid = new HashMap<>();
        ArrayList<Atom> tempAminoacidArray = new ArrayList<>();
        CellGeneric tempCell = new CellGeneric();
        String cellId;
        //optimizar?
        for(AminoacidExtended aminoacidEx: Aminoacids){
            for(Atom a: aminoacidEx.getAminoacid().getAtoms()){
                //Calculates the cell where the atom belongs
                cellId = getStringKeyCell(interval, a.getCoords());
                
                //Insert the aminoacid in his respective cell. 
                //If the cell doesnt exist create it, otherwise add the aminoacidEx to the existing ones in the cell
                tempCell = getGrid().get(cellId)!=null ?  getGrid().get(cellId) : new CellGeneric();
                tempAminoacidArray = tempCell.getElements();
                tempAminoacidArray.add(a);
                tempCell.setElements(tempAminoacidArray);
                getGrid().put(cellId, tempCell);
            }
        }
        return getGrid();
    }
    
    /**
    * Obtains the highest and lowest point on the x, y and z axis in a matrix of 2x3
    * [0][0] minimum value of X, [0][1] minimum value of Y, [0][2] minimum value of Z,
    * @param atoms the atoms to process
    * @return A matrix of 2x3 with the highest and lowest point of the axis(x,y,z).
    */
    private double[] obtainMinBounds(Collection<AminoacidExtended> aminoacids){
        double[]  minCoords = {Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY};
        double[] tempCoords = new double[3];
        for(AminoacidExtended a: aminoacids){
            tempCoords = a.getCoords();
            if( tempCoords[0] < minCoords[0] )  //minimum value of X
                minCoords[0] = tempCoords[0];
            if( tempCoords[0] < minCoords[1] )  //minimum value of Y
                minCoords[1] = tempCoords[1];
            if( tempCoords[0] < minCoords[2] )  //minimum value of Z
                minCoords[2] = tempCoords[2];
        }
        return minCoords;
    }

    
 
    //modificado armando 
    public ArrayList<AminoacidExtended> searchCollisionInCellAndNeighborhoodAminoacids(CellGeneric cell, AminoacidExtended amino, double interval, HashMap<String,CellGeneric> _Grid){
         ArrayList<AminoacidExtended> aminoacidsToVerify = new ArrayList<>();
         ArrayList<Atom> tempArray = new ArrayList<>();
         ArrayList<CellGeneric> neighborhood = new ArrayList<>(); 
         
         tempArray = cell.getElements();
         if(tempArray.size()>1)
             aminoacidsToVerify = allAgainstAllComparision(amino, tempArray, aminoacidsToVerify);
         
         neighborhood = obtainNeighborsGeneric(getIntKeyCell(interval, amino.getCoords()), _Grid);
         
         for(CellGeneric neighbor: neighborhood)
            aminoacidsToVerify =  oneAgainstAllcomparison(amino, neighbor.getElements(), aminoacidsToVerify);
         
         return aminoacidsToVerify;
    }
    
    //Modificado Hector 
    public ArrayList<String> obtainAminoacidsToVerifyInCellAndNeighborhood(AminoacidExtended amino, double interval/*, HashMap<String,CellGeneric> _Grid*/){
         ArrayList<String> aminoacidsToVerify = new ArrayList<>();
         ArrayList<Atom> tempArray = new ArrayList<>();
         ArrayList<CellGeneric> neighborhood = new ArrayList<>(); 
         String cellId = getStringKeyCell(interval, amino.getCoords());
         CellGeneric cell = /*_*/getGrid().get(cellId);
         
         tempArray = cell.getElements();
         if(tempArray.size()>1)
             aminoacidsToVerify = obtainAminoacidsToVerifyInArray(amino, tempArray, aminoacidsToVerify);//allAgainstAllComparision(amino, tempArray, aminoacidsToVerify);
         
         neighborhood = obtainNeighborsGeneric(getIntKeyCell(interval, amino.getCoords()), getGrid());
         
         for(CellGeneric neighbor: neighborhood)
            aminoacidsToVerify =  obtainAminoacidsToVerifyInArray(amino, neighbor.getElements(), aminoacidsToVerify);//oneAgainstAllcomparison(amino, neighbor.getElements(), aminoacidsToVerify);
//         System.out.println(aminoacidsToVerify.size());
         return aminoacidsToVerify;
    }
    public ArrayList<String> obtainAminoacidsToVerifyInCellAndNeighborhoodALL(AminoacidExtended amino, double interval/*, HashMap<String,CellGeneric> _Grid*/){
         ArrayList<String> aminoacidsToVerify = new ArrayList<>();
         ArrayList<Atom> tempArray = new ArrayList<>();
         Object[] values;
         values=getGrid().values().toArray();
         for(int i=0;i<values.length;i++){
             tempArray=((CellGeneric)values[i]).getElements();
             for(int j=0;j<tempArray.size();j++){
                 if(!aminoacidsToVerify.contains(tempArray.get(j).getParent().getPDBCode()))
                     if(!tempArray.get(j).getParent().getPDBCode().equals(amino.getAminoacid().getPDBCode()))
                        aminoacidsToVerify.add(tempArray.get(j).getParent().getPDBCode());
             }
         }
         return aminoacidsToVerify;
    }
 
    
    //modificado armando
    public ArrayList<AminoacidExtended> allAgainstAllComparision(AminoacidExtended amino, ArrayList<Atom> Atoms, ArrayList<AminoacidExtended> aminoacidsToVerify){
        double distance = 0.0, MIN_DISTANCE = 0.0, r1 = 0.0, r2 = amino.getRatio();
        AminoAcid am1, am2;
        
        try {
            Object[] atoms = Atoms.toArray();
            for (int i = 0; i < atoms.length; i++) {
                am1 = (AminoAcid)((Atom) atoms[i]).getParent(); 
                if(!amino.equals(am1)) {
                     if(aminoacidsToVerify.contains(i)){
                         
                     }
                }
            }//end for
        } catch (Exception ex) {
            System.err.print(ex.toString());
        }
        
        return aminoacidsToVerify;
    }
    
    /** Dado un aminoacido a y un conjunto de aminoacidos A y se obtiene A' \subset A, tal que A' contiene todos los aminoacidos de A que se encuentran a una distancia 
     * menor o igual que el radio de a
     * @param aminoacid objecto aminoacido con el cual se comparan los aminoacidos
     * @param Aminoacids conjunto de aminoacidos a evaluar si estan a una distancia menor que el radio
     * @param aminoacidsToVerify resultado que se se pasa por parametro
     * @return aminoacidos resultantes que estan a una distancia menor que el radio del aminoacido a comparar
     */
    public ArrayList<String> obtainAminoacidsToVerifyInArray(AminoacidExtended aminoacid, ArrayList<Atom> Atoms, ArrayList<String> aminoacidsToVerify){
        double distance = 0.0, MIN_DISTANCE = 0.0, r1 = 0.0, r2 = aminoacid.getRatio();
        AminoAcid am1;
        
        try {
            Object[] atoms = Atoms.toArray();
            for (int i = 0; i < atoms.length; i++) {
                am1 = (AminoAcid)((Atom) atoms[i]).getParent(); 
                if(!aminoacid.getAminoacid().getPDBCode().equals(am1.getPDBCode())) {
                     if(!aminoacidsToVerify.contains(am1.getPDBCode())){
                         aminoacidsToVerify.add(am1.getPDBCode());
                     }
                }
            }//end for
        } catch (Exception ex) {
            System.err.print(ex.toString());
        }
        
        return aminoacidsToVerify;
    }
    
    public ArrayList<ArrayList<AminoacidExtended>> allAgainstAllComparison(ArrayList<AminoacidExtended> Aminoacids, ArrayList<ArrayList<AminoacidExtended>> aminoacidsToVerify){
        //ArrayList<ArrayList<AminoacidExtended>> aminoacidsToVerify = new ArrayList<ArrayList<AminoacidExtended>>();
        ArrayList<AminoacidExtended> tempArray = new ArrayList<>();
        double distance = 0.0, MIN_DISTANCE = 0.0, r1 = 0.0, r2 = 0.0;
        //int collisionCounter = 0;
        AminoacidExtended am1, am2;
        try {
            Object[] aminoacids = Aminoacids.toArray();
            for (int i = 0; i < aminoacids.length; i++) {
                for (int j = i+1; j < aminoacids.length; j++) {
                    am1 = (AminoacidExtended) aminoacids[i];
                    am2 = (AminoacidExtended) aminoacids[j];
                    
                    distance = Calc.getDistance(am1.getCentralAtom(), am2.getCentralAtom());

                    //need to update the ratio for the actual ratio in the residue
                    r1 = am1.getRatio();
                    r2 = am2.getRatio();

                    MIN_DISTANCE = r1 + r2;

                    if (distance <= MIN_DISTANCE) {
                        //validate its collisions with their inner atoms                        
                        tempArray = new ArrayList<>();
                        tempArray.add(am1);
                        tempArray.add(am2);
                        aminoacidsToVerify.add(tempArray);
                    }
                }
            }
        } catch (Exception ex) {
            System.err.print(ex.toString());
        }
        
        return aminoacidsToVerify;
    }
    
    //modificado by armando
    //solo retorna los aminoacidos que colisionan con el aminoacido aminoEx
    private ArrayList<AminoacidExtended> oneAgainstAllcomparison(AminoacidExtended aminoacidEx, ArrayList<AminoacidExtended> Aminoacids, ArrayList<AminoacidExtended> aminoacidsToVerify){
        double distance = 0.0, MIN_DISTANCE = 0.0, r1 = 0.0, r2 =0.0;
        AminoacidExtended aminoacidEx_i;
        
       try {
            Object[] aminoacids = Aminoacids.toArray();
            for (int i = 0; i < aminoacids.length; i++) {
                aminoacidEx_i = (AminoacidExtended) aminoacids[i];
                
                distance = Calc.getDistance(aminoacidEx.getCentralAtom(), aminoacidEx_i.getCentralAtom());

                //need to update the ratio for the actual ratio in the residue
                r1 = aminoacidEx.getRatio();
                r2 = aminoacidEx_i.getRatio();

                MIN_DISTANCE = r1 + r2;

                if (distance <= MIN_DISTANCE) 
                      aminoacidsToVerify.add(aminoacidEx_i);

            }
        } catch (Exception ex) {
            System.err.print(ex.toString());
        }
        
        return aminoacidsToVerify;
    }
    
    private ArrayList<ArrayList<AminoacidExtended>> oneAgainstAllComparison(AminoacidExtended aminoacidEx, ArrayList<AminoacidExtended> Aminoacids, ArrayList<ArrayList<AminoacidExtended>> aminoacidsToVerify){
        //ArrayList<CollisionAminoacid>collisionAminoacid = collisionAminoacids;
        ArrayList<AminoacidExtended> tempArray = new ArrayList<>();
        double distance = 0.0, MIN_DISTANCE = 0.0, r1 = 0.0, r2 = 0.0;
        AminoacidExtended aminoacidEx_i;
        
        try {
            Object[] aminoacids = Aminoacids.toArray();
            for (int i = 0; i < aminoacids.length; i++) {
                aminoacidEx_i = (AminoacidExtended) aminoacids[i];
                
                distance = Calc.getDistance(aminoacidEx.getCentralAtom(), aminoacidEx_i.getCentralAtom());

                //need to update the ratio for the actual ratio in the residue
                r1 = aminoacidEx.getRatio();
                r2 = aminoacidEx_i.getRatio();

                MIN_DISTANCE = r1 + r2;

                if (distance <= MIN_DISTANCE) {

                    tempArray = new ArrayList<>();
                    tempArray.add(aminoacidEx);
                    tempArray.add(aminoacidEx_i);
                    //save the aminoacids to validate its collisions with their inner atoms     
                    aminoacidsToVerify.add(tempArray);
                }

            }
        } catch (Exception ex) {
            System.err.print(ex.toString());
        }
        
        return aminoacidsToVerify;
    }
        
    /**
    * Obtain all the neighbors of a given cell with cellKeys in the Grid.
    * (for a k-dimensional space there are (3^k-1) neighbors, since it is a 3-dimensional space, we are going to obtain 26 neighbors cells.)
    * @param cellKeys an integer array with the keys of the atom to look up for its neighborhood
    * @param Grid the container of all atoms separated by cells
    * @return an array of all the neighbors of the atom, this can be empty if there is no cell around the cell of the atom.
    */ 
    public ArrayList<CellGeneric> obtainNeighborsGeneric(int[] cellKeys, HashMap<String, CellGeneric> Grid){
        ArrayList<CellGeneric> neighborhood = new ArrayList<>();
        CellGeneric tempCell = new CellGeneric();
        String [] neighborsKeys = new String[26];
        //            (a, b, c+1) (a, b, c-1)
        neighborsKeys[0] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[1] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]-1);
        //(a, b+1, c) ..c+1) ..c-1)
        neighborsKeys[2] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[3] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[4] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]-1);
        //(a+1, b+1, c) ..c+1) ..c-1)
        neighborsKeys[5] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[6] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[7] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]-1);
        //(a-1, b+1, c) ..c+1) ..c-1)
        neighborsKeys[8] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[9] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[10] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]+1)+","+Integer.toString(cellKeys[2]-1);
        //(a, b-1, c) ..c+1) ..c-1)
        neighborsKeys[11] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[12] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[13] = Integer.toString(cellKeys[0])+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]-1);
        //(a+1, b-1, c) ..c+1) ..c-1)
        neighborsKeys[14] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[15] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[16] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]-1);
        //(a-1, b-1, c) ..c+1) ..c-1)
        neighborsKeys[17] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]);
        neighborsKeys[18] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[19] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1]-1)+","+Integer.toString(cellKeys[2]-1);
        //(a+1, b, c) ..c+1) ..c-1)
        neighborsKeys[20] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]);
        neighborsKeys[21] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[22] = Integer.toString(cellKeys[0]+1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]-1);
        //(a-1, b, c) ..c+1) ..c-1)
        neighborsKeys[23] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]);
        neighborsKeys[24] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]+1);
        neighborsKeys[25] = Integer.toString(cellKeys[0]-1)+","+Integer.toString(cellKeys[1])+","+Integer.toString(cellKeys[2]-1);

        for(String neighKey: neighborsKeys){
            if(Grid.containsKey(neighKey)){
                tempCell = Grid.get(neighKey);
                if (!tempCell.isEvaluated())
                    neighborhood.add(Grid.get(neighKey));
            }
        }

        return neighborhood;
    }
    
    /**
    * Method used in Rabin and also in Fortune & Hopcroft algorithms, find the (cell) bucket of the atom
    * @param interval an array with the size of each side of the cell (bucket).
    * @param coordinates the coordinates of the atom
    * @param origin the coordinates of the origin point (minimum values of all axis points) to generate the Grid or Buckets container
    * @param intervalMethod the method that defines how to create the Grid with the interval [false: floor(x-x_0)/delta | true: floor(x/delta)]
    * @return the String that represents the key of the cell (bucket) where the atom is going to be stored.
    */ 
    public String getStringKeyCell(double interval, double [] coordinates){
       int [] keys = this.getIntKeyCell(interval, coordinates);                        
      
       return Integer.toString(keys[0])+","+Integer.toString(keys[1])+","+Integer.toString(keys[2]);
    }
    
    public int[] getIntKeyCell(double interval, double [] coordinates){
       int [] keys = new int[3];
       for(int i=0; i<keys.length; i++)
           keys[i] = (int) Math.floor(coordinates[i]/interval);                               
      
       return keys;
    }
    public double getRotamerFrequencyRASP(String aminoPdbName, int rot){
        double sum = 0.0, gamma=0, rotamerProb = 0.0, frequency = 0.0, EPSILON  = 0.00001;
        RotamerLibrary rl;
  
        //for(String threeLetterCode: aminoPdbCodes) {
        //se valida los aminoacidos GLY y ALA, por que no tienen angulos de torsion en la cadena lateral
        if (!(aminoPdbName.equalsIgnoreCase("GLY") || aminoPdbName.equalsIgnoreCase("ALA"))) {
            rl = rlc.getRotamerLibrary(aminoPdbName);

            if(aminoPdbName.equalsIgnoreCase("ARG"))
                gamma=1.4;
            if(aminoPdbName.equalsIgnoreCase("ASN"))
                gamma=0.9;
            if(aminoPdbName.equalsIgnoreCase("ASP"))
                gamma=1.3;
            if(aminoPdbName.equalsIgnoreCase("CYS"))
                gamma=1;
            if(aminoPdbName.equalsIgnoreCase("GLN"))
                gamma=1.1;
            if(aminoPdbName.equalsIgnoreCase("GLU"))
                gamma=1.2;
            if(aminoPdbName.equalsIgnoreCase("HIS"))
                gamma=1.9;
            if(aminoPdbName.equalsIgnoreCase("ILE"))
                gamma=1;
            if(aminoPdbName.equalsIgnoreCase("LEU"))
                gamma=1.1;
            if(aminoPdbName.equalsIgnoreCase("LYS"))
                gamma=1;
            if(aminoPdbName.equalsIgnoreCase("MET"))
                gamma=1.1;
            if(aminoPdbName.equalsIgnoreCase("PHE"))
                gamma=2.1;
            if(aminoPdbName.equalsIgnoreCase("PRO"))
                gamma=0.5;
            if(aminoPdbName.equalsIgnoreCase("SER"))
                gamma=0.9;
            if(aminoPdbName.equalsIgnoreCase("THR"))
                gamma=1.5;
            if(aminoPdbName.equalsIgnoreCase("TRP"))
                gamma=1;
            if(aminoPdbName.equalsIgnoreCase("TYR"))
                gamma=1;
            if(aminoPdbName.equalsIgnoreCase("VAL"))
                gamma=1;

            rotamerProb = rl.getRotamer().get( rot ).getProbabilityValue();
            if(rotamerProb < EPSILON)
                rotamerProb = 0.001;

            sum = gamma * Math.log10(rotamerProb / ListUtil.MaxRotamerProb(aminoPdbName));
        }
        //}
        frequency = (-sum)*(0.5);
//        if(frequency<0 || frequency>1)
//            System.out.println("-"+aminoPdbName+" con rot "+rot+" = "+rotamerProb +" / "+ ListUtil.MaxRotamerProb(aminoPdbName));
        return frequency;
    }
    
    public double getRotamerFrequency(String aminoPdbName, int aminoPdbCode,int rot){
        double sum = 0.0, gamma, rotamerProb = 0.0, frequency = 0.0, EPSILON  = 0.00001;
        RotamerLibrary rl;
        boolean isSpecialAminoacid = false;
  
        //for(String threeLetterCode: aminoPdbCodes) {
        //se valida los aminoacidos GLY y ALA, por que no tienen angulos de torsion en la cadena lateral
        if (!(aminoPdbName.equalsIgnoreCase("GLY") || aminoPdbName.equalsIgnoreCase("ALA"))) {
            rl = rlc.getRotamerLibrary(aminoPdbName);

            isSpecialAminoacid = (aminoPdbName.equalsIgnoreCase("PHE") || aminoPdbName.equalsIgnoreCase("TYR") || 
                                  aminoPdbName.equalsIgnoreCase("TRP") || aminoPdbName.equalsIgnoreCase("HIS"));
            gamma = isSpecialAminoacid ? 2.0 : 1.0;
            
//            System.out.println("name:"+aminoPdbName+" - pbccode: "+aminoPdbCode+ " - type:"+rl.getAminoAcidType()+" --- "+ Integer.parseInt( sol.getRomaterVector().get(0).get(aminoPdbCode-1) ));
//            int aminoN=Integer.parseInt( sol.getRomaterVector().get(0).get(aminoPdbCode-1));
//            try{
                rotamerProb = rl.getRotamer().get( rot ).getProbabilityValue();
                
//            }catch(Exception e){
//                System.out.println("Detenido en:" + aminoPdbCode);
//                System.exit(0);
//            }
            if(rotamerProb < EPSILON)
                rotamerProb = 0.001;

            sum = gamma * Math.log10(rotamerProb / ListUtil.MaxRotamerProb(aminoPdbName));
        }
        //}
        frequency = -sum;
        return getRotamerFrequencyRASP(aminoPdbName,rot);
    }

    /**
     * @return the Grid
     */
    public HashMap<String, CellGeneric> getGrid() {
        return Grid;
    }
    public void setGrid(HashMap<String, CellGeneric> grid) {
        Grid=grid;
    }
    
    public AminoAcid getAminoacid(int residue){
        return aminoacidMap.get(residue+1).getAminoacid()!=null?aminoacidMap.get(residue+1).getAminoacid():null;
    }
    
}
